package AssetDepreciation

import (
	"ToolLibray/jwtx"
	"context"
	"fmt"
	"math"
	"property_api/internal/svc"
	"property_api/internal/types"
	"property_api/utils/xform"
	"property_service/property_service"
	"property_service/utils/BeanUtils"
	"sync"
	"time"

	"github.com/zeromicro/go-zero/core/logx"
)

type BatchGenerateDepreciationLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewBatchGenerateDepreciationLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BatchGenerateDepreciationLogic {
	return &BatchGenerateDepreciationLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

// GetDepreciationByTime 函数用于获取当前时间,并计算出折旧额
/**
 * @param StartDate 折旧开始时间
 * @param EndDate 折旧结束时间
 * @param ExpectedUsefulLife 预计使用年限
 * @param DepreciationRate 月折旧率
 * @param AssetValue 资产原值

 * @return AccumulatedDepreciationValue 累计折旧额
 * @return NetResidueValue 残值(剩余价值)
 * @return DepreciatedMonths 已折旧月数
 */
func GetDepreciationByTime(StartDate time.Time, EndDate time.Time, ExpectedUsefulLife int32, DepreciationRate float64, AssetValue float64) (AccumulatedDepreciationValue float64, NetResidueValue float64, DepreciatedMonths int64) {
	// 计算时间差
	y1, m1, _ := StartDate.Date()
	y2, m2, _ := EndDate.Date()
	//获取累计折旧额
	// 计算两个时间点之间的月份差作为 float64 类型，用于折旧计算
	d := (y1-y2)*12 + int(m1-m2)
	if d < 0 {
		d = -d
	}
	//判断是不是最后一个月，最后一个月将所有折旧额使用
	if ExpectedUsefulLife == int32(d) {
		//如果预计使用月份等于当前折旧月份，则将所有折旧额使用
		AccumulatedDepreciationValue = AssetValue
		NetResidueValue = 0
		return AccumulatedDepreciationValue, NetResidueValue, int64(d)
	}
	// 根据净值、折旧率和时间间隔计算累计折旧额
	AccumulatedDepreciationValue = Round(AssetValue*DepreciationRate*float64(d)/100, 3)
	// 计算折旧额
	NetResidueValue = Round(AssetValue-AccumulatedDepreciationValue, 3)
	//  返回结果  NetResidueValue  残值（净值-折旧额）,AccumulatedDepreciationValue  累计折旧额（月折旧率*原值）, int64(months)   折旧月数
	return NetResidueValue, AccumulatedDepreciationValue, int64(d)
}

// Round 保留 n 位小数
func Round(f float64, n int) float64 {
	pow := math.Pow(10, float64(n))
	return math.Round(f*pow) / pow
}

// AddAssetDepreciation BatchGenerateDepreciationLogic 结构体的方法，用于批量添加资产折旧信息
// 参数:
//   - DepreciationInfos: 资产折旧信息列表
//   - req: 资产折旧ID请求
//
// 返回值:
//   - Msg: 错误信息列表
//   - les: 成功添加的折旧信息数量
func (l *BatchGenerateDepreciationLogic) AddAssetDepreciation(DepreciationInfos []*property_service.DepreciationInfo, req *types.AssetDepreciationIDs) (Msg []string, les int) {

	// 初始化变量
	lens := 0         // 成功计数器
	var MsgG []string // 错误信息收集数组
	// 设置租户ID，从上下文中获取
	tenantID := jwtx.GetUserTenantId64(l.ctx)
	// 设置创建用户和更新用户，从上下文中获取
	userIdStr := xform.Int64ToString(jwtx.GetUserId(l.ctx))

	// 批量获取所有资产信息，避免循环中逐个查询
	assetIDs := make([]int64, len(DepreciationInfos))
	for i, v := range DepreciationInfos {
		assetIDs[i] = v.AssetID
	}

	// 批量查询资产信息
	assetRes, err := l.svcCtx.AssetService.AssetBasicInfoPage(l.ctx, &property_service.AssetBasicInfoPageReq{
		Page:  0,
		Limit: 0,
		Where: &property_service.AssetBasicInfo{
			AssetIds: assetIDs,
			TenantID: tenantID,
		},
	})

	if err != nil {
		return []string{fmt.Sprintf("批量查询资产信息异常: %s", err.Error())}, 0
	}

	// 将资产信息转换为map，便于快速查找
	assetInfoMap := make(map[int64]*property_service.AssetBasicInfo)
	for _, asset := range assetRes.List {
		assetInfoMap[asset.ID] = asset
	}

	// 收集所有需要添加的折旧记录
	var depreciationList []*property_service.AssetDepreciation

	//如果查询成功，则遍历查询结果，将已经折旧的资产id，添加到数组中
	for _, v := range DepreciationInfos {
		msg := ""
		if v.DepreciationRate <= 0 || v.DepreciationRateYear <= 0 {
			msg = fmt.Sprintf("%s 折旧率或年折旧率不能小于等于0", msg)
		}

		// 从map中获取资产信息，而不是每次都查询数据库
		AssetBasicInfo, exists := assetInfoMap[v.AssetID]
		if !exists {
			msg = fmt.Sprintf(" 资产查询不存在,异常资产id:%v", v.ID)
			MsgG = append(MsgG, msg)
			continue
		}

		DepreciationInfo := v
		//======================开始计算================================
		//计算折旧  AccumulatedDepreciationValue 累计折旧额  NetResidueValue 残值(剩余价值)  DepreciatedMonths 已折旧月数
		AccumulatedDepreciationValue, NetResidueValue, DepreciatedMonths := GetDepreciationByTime(BeanUtils.GetDateFormat(req.DepreciationDate), BeanUtils.GetDateFormat(DepreciationInfo.StartDate), DepreciationInfo.ExpectedUsefulLife, DepreciationInfo.DepreciationRate, DepreciationInfo.AssetValue)
		//计算残值
		Residual := 100 - DepreciationInfo.DepreciationRate*float64(DepreciatedMonths)
		//获取卡片编码
		cc, err := l.svcCtx.BasicService.GetNo(l.ctx, &property_service.DeviceParameterConfigby{
			ParamCode:     "asset_depreciation", //参数编码（唯一标识）
			Zdm:           "card_number",
			DateZdm:       "depreciation_period",
			Date:          "",
			DepartmentId:  fmt.Sprintf("%d", AssetBasicInfo.UsageRoomID),
			DepartmentZdm: "using_dept_id",
			StockId:       fmt.Sprintf("%d", req.StockID),
			TenantID:      tenantID,
		})
		// 如果服务层返回错误，返回统一的错误响应
		if err != nil {
			msg = fmt.Sprintf("%s 查询编码异常", msg)
		}
		Depreciation := 0.0
		//如果剩余使用月份等于0 则 月折旧额=资产原值-（月折旧率*（预计使用月份-1）） 也就是最后一个月折旧额等于剩余折旧额
		if int32(DepreciatedMonths) == DepreciationInfo.ExpectedUsefulLife {
			Depreciation = AssetBasicInfo.AssetValue - (DepreciationInfo.DepreciationRate * float64(DepreciationInfo.ExpectedUsefulLife-1) / 100)
		} else {
			Depreciation = Round(DepreciationInfo.DepreciationRate*AssetBasicInfo.AssetValue/100, 3)
		}
		//如果两个都没有报错，则开始计算折旧，并且更改资产信息表中的资产折旧信息
		//如果有错误信息，那么不执行新增
		if msg == "" && len(Msg) == 0 {
			// 构建资产折旧信息对象
			depreciation := &property_service.AssetDepreciation{
				//资产ID
				AssetID:                  AssetBasicInfo.ID,
				TenantID:                 tenantID,
				CardNumber:               cc.Str,                                                         // 卡片编号
				AssetName:                AssetBasicInfo.AssetName,                                       // 资产名称
				AssetCategory:            AssetBasicInfo.AssetCategory,                                   // 资产分类
				AssetCategoryID:          AssetBasicInfo.AssetCategoryID,                                 // 资产分类ID
				AssetCode:                AssetBasicInfo.AssetCode,                                       // 资产编码
				DepreciationPeriod:       req.DepreciationDate,                                           // 折旧期间
				UsingDept:                AssetBasicInfo.UsageRoom,                                       // 使用科室
				UsingDeptID:              AssetBasicInfo.UsageRoomID,                                     // 使用科室ID
				OriginalValue:            AssetBasicInfo.AssetValue,                                      // 资产原值
				AccumulatedDepreciation:  AccumulatedDepreciationValue,                                   // 累计折旧额
				Depreciation:             Depreciation,                                                   // 本月折旧额
				DepreciationMethod:       DepreciationInfo.Method,                                        // 折旧方法
				PurchaseDate:             DepreciationInfo.StartDate,                                     // 购置日期
				ExpectedUsefulLife:       DepreciationInfo.ExpectedUsefulLife - int32(DepreciatedMonths), //剩余使用年限（月）
				DepreciatedMonths:        int32(DepreciatedMonths),                                       //已折旧月份
				UsefulLife:               DepreciationInfo.ExpectedUsefulLife,                            // 预计使用年限（月）
				ResidualValue:            NetResidueValue,                                                // 残值
				StatusID:                 DepreciationInfo.StatusID,                                      // 状态ID（关联状态表）
				DepreciationStatus:       "0",                                                            // 折旧状态（0-预折旧 1-折旧中 2-已折旧）
				StockID:                  req.StockID,                                                    // 仓库ID
				CreatedAtuser:            userIdStr,                                                      // 创建人
				UpdatedAtuser:            userIdStr,                                                      // 更新人
				DepreciationRate:         DepreciationInfo.DepreciationRate,                              // 月折旧率
				DepreciationRateYear:     DepreciationInfo.DepreciationRateYear,                          // 年折旧率
				Residual:                 Residual,                                                       // 残值率
				CurrentMonthDepreciation: Depreciation,                                                   // 本月折旧=月折旧率*净值
				//======================结束计算================================
			}

			// 添加到批量插入列表
			depreciationList = append(depreciationList, depreciation)
			lens++
		}
		if len(msg) != 0 {
			msg = fmt.Sprintf(" 资产:%s异常,异常资产id:%v,异常信息: %s", AssetBasicInfo.AssetName, v.ID, msg)
			MsgG = append(MsgG, msg)
		}
	}

	// 批量插入所有折旧记录
	if len(depreciationList) > 0 {
		//调用批量新增方法
		_, err := l.svcCtx.AssetService.AssetDepreciationAddList(l.ctx, &property_service.AssetDepreciationList{
			List: depreciationList,
		})
		if err != nil {
			MsgG = append(MsgG, fmt.Sprintf("批量新增折旧信息异常: %s", err.Error()))
		}
		/*		// 分批插入，每批100条记录
				batchSize := 100
				for i := 0; i < len(depreciationList); i += batchSize {
					end := i + batchSize
					if end > len(depreciationList) {
						end = len(depreciationList)
					}

					batch := depreciationList[i:end]

					// 创建批量插入请求
					for _, dep := range batch {
						_, err := l.svcCtx.AssetService.AssetDepreciationAdd(l.ctx, dep)
						if err != nil {
							MsgG = append(MsgG, fmt.Sprintf("添加折旧信息异常: %s", err.Error()))
							lens-- // 减少成功计数
						}
					}
				}*/
	} else {
		MsgG = append(MsgG, fmt.Sprintf("未生成指定资产折旧信息"))
	}

	return MsgG, lens
}

func (l *BatchGenerateDepreciationLogic) BatchGenerateDepreciation(req *types.AssetDepreciationIDs) (resp *types.Response, err error) {
	var DepreciationInfos []*property_service.DepreciationInfo
	var ids []int64
	//判断是否是全部折旧 0：全部折旧 1：部分折旧
	if req.IsAll == 1 {
		if len(req.AssetDepreciationID) == 0 {
			return &types.Response{
				Code:    500,
				Data:    0,
				Message: fmt.Sprintf("请传输数据"),
			}, nil
		} else {
			ids = req.AssetDepreciationID
			fmt.Println("获取到的数组长度:	", len(req.AssetDepreciationID))
		}
	}
	// 设置租户ID，从上下文中获取
	tenantID := jwtx.GetUserTenantId64(l.ctx)

	//获取折旧如期
	if req.DepreciationDate != "" {
		// 如果req.DepreciationDate不为空，则验证日期格式
		_, err := time.Parse("2006-01", req.DepreciationDate)
		if err != nil {
			return &types.Response{
				Code:    500,
				Data:    0,
				Message: fmt.Sprintf("日期格式错误: %s", req.DepreciationDate),
			}, nil
		}
	}

	//查询指定折旧的资产
	res, err := l.svcCtx.AssetService.DepreciationInfoPage(l.ctx, &property_service.DepreciationInfoPageReq{
		Page:  0,
		Limit: 0,
		Where: &property_service.DepreciationInfo{
			Ids:        ids,         //前端传输进来的
			StatusID:   req.StockID, //仓库ID
			TenantID:   tenantID,    //租户ID
			StatusCode: "0",         // 折旧状态 0：正常折旧 1：已报废
		},
	})
	//如果查询失败，则返回错误
	if err != nil {
		return &types.Response{
			Code:    500,
			Data:    nil,
			Message: fmt.Sprintf("查询资产折旧信息异常%s", err.Error()),
		}, nil
	}
	if len(res.List) == 0 {
		return &types.Response{
			Code:    500,
			Data:    nil,
			Message: fmt.Sprintf("未查询到指定资产信息"),
		}, nil
	}
	DepreciationInfos = res.List
	//删除指定资产记录 根据折旧月份+资产id(如果是全部则删除指定月份)
	_, err = l.svcCtx.AssetService.AssetDepreciationDel(l.ctx, &property_service.AssetDepreciation{
		DepreciationPeriod: req.DepreciationDate,
		AssetIDs:           ids,
	})
	if err != nil {
		return &types.Response{
			Code:    500,
			Data:    nil,
			Message: fmt.Sprintf("删除指定资产折旧信息异常%s", err.Error()),
		}, nil
	}

	//输出数组长度
	fmt.Println("获取到的数组长度:	", len(DepreciationInfos))

	//获取长度,看看是否需要进行多线程处理
	lens := 0
	//报错数据
	var Msg []string
	if len(DepreciationInfos) < 250 {
		msg, lens := l.AddAssetDepreciation(DepreciationInfos, req)
		fmt.Println("全部处理完成")
		fmt.Println("Msg长度:", len(Msg))
		if len(msg) != 0 {
			return &types.Response{
				Code:    404,
				Data:    nil,
				Message: fmt.Sprintf("批量生成折旧记录错误:%s", msg),
			}, nil
		} else {
			return &types.Response{
				Code:    200,
				Data:    nil,
				Message: fmt.Sprintf("批量生成折旧记录成功,成功条数:%d", lens),
			}, nil
		}
		//数据量大则进行多线程处理
	} else {
		// 2. 获取多少线程 300的倍数
		n := len(DepreciationInfos) / 250
		fmt.Printf("======================共:%d条数据   启动: %d 条线程==============", len(DepreciationInfos), n)
		// 3. 计算每份大小
		total := len(DepreciationInfos)
		batch := total / n
		if total%n != 0 {
			batch++ // 向上取整，保证全量覆盖
		}
		var wg sync.WaitGroup
		// 3. 分批处理
		for i := 0; i < n; i++ {
			start := i * batch
			end := start + batch
			if end > total {
				end = total
			}
			wg.Add(1)

			// 3. 启动 goroutine
			i := i
			go func(threadNo, s, e int) {
				defer wg.Done()
				// 4. 切片传进去：共享底层数组，零拷贝
				msg, i := l.AddAssetDepreciation(DepreciationInfos[s:e], req)

				if len(msg) != 0 {
					Msg = append(Msg, fmt.Sprintf("线程:%d 生成折旧记录错误:%s", threadNo, msg))
				}
				lens += i
			}(i, start, end)
		}
		wg.Wait()
		fmt.Println("全部处理完成")
		fmt.Println("Msg长度:", len(Msg))
		if len(Msg) != 0 {
			fmt.Println(fmt.Sprintf("错误条数：%d", len(Msg)))
			return &types.Response{
				Code:    404,
				Data:    lens,
				Message: fmt.Sprintf("批量生成折旧记录错误:%s", Msg),
			}, nil
		} else {
			return &types.Response{
				Code:    200,
				Data:    lens,
				Message: fmt.Sprintf("批量生成折旧记录成功,成功条数:%d", lens),
			}, nil
		}
	}
}
